using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MySql.Data;
using System.Data;
using System.Windows.Forms;

namespace DnDDB
{
    public class Character{
    	
	    // non-derived variables
	    // non derived variables will have accessors and mutators
	    // in addition, non derived variables which are collections will have
	    // and add method as well
	    List<string> languages;
	    List<string> feats;
	    List<Spell> known_spells;
	    List<Equipment> equipment; // We could wrap armor and weapons into here
							      // but then we'd need to go through the entire
							      // list to get the weapons and armor
									// new note - weapons and armor are included - armor U equipment = equipment
									// to make the SQL simpler due to the construction of the tables.
	    List<Armor> armor;
	    List<Weapon> weapons;
	    Dictionary<string, int> class_levels; // stores class and number of levels 
	                                          // taken in the class
        Dictionary<string, int> alignment_dictionary;
        Dictionary<int, string> alignment_dictionary_reverse;

    	getMySQL get = new getMySQL();
	    string name;
	    string race;
	    int copper;
	    int cur_hp;
	    int max_hp;
	    int str;
	    int dex;
	    int con;
	    int wis;
	    int intelligence; // can't just call it int
	    int cha;
        string alignment;
    	
	    // derived variables
	    // derived variables will only have accessors
	    int bab;
	    int fort;
	    int will;
	    int reflex;
	    int ac;


        public Character(string name)
        {
            this.setupAlignmentDictionary();
            this.armor = new List<Armor>();
            this.equipment = new List<Equipment>();
            this.weapons = new List<Weapon>();
            this.known_spells = new List<Spell>();
            this.class_levels = new Dictionary<string, int>();
			this.languages = new List<String>();
			this.feats = new List<String>();
            // insert database stuff here
            this.name = name;
            this.copper = 0;
            this.cur_hp = 0;
            this.max_hp = 0;
            this.str = 0;
            this.dex = 0;
            this.con = 0;
            this.wis = 0;
            this.intelligence = 0;
            this.cha = 0;
            this.race = "Human";
            this.alignment = "Lawful Good";
        }
    	
	    public Character ( List<string> languages, List<string> feats,
		    List<Spell> known_spells, List<Equipment> equipment,
		    List<Armor> armor, List<Weapon> weapons, 
		    Dictionary<string, int> class_levels, string name,
		    string race, int copper, int cur_hp, int max_hp,
		    int str, int dex, int con, int wis,
		    int intelligence, int cha ) {
                this.setupAlignmentDictionary();
			    // set all the derived attributes
			    this.languages = languages;
			    this.feats = feats;
			    this.known_spells = known_spells;
			    this.equipment = equipment;
			    this.armor = armor; 
			    this.weapons = weapons; 
			    this.class_levels = class_levels;
			    this.name = name;
			    this.race = race;
			    this.copper = copper;
			    this.cur_hp = cur_hp;
			    this.max_hp = max_hp;
			    this.str = str;
			    this.dex = dex;
			    this.con = con;
			    this.wis = wis;
			    this.intelligence = intelligence;
			    this.cha = cha;
    			
			    // yay!  now onto the derived attributes!
			    //deriveAttributes();
		    }
    		
            private void setupAlignmentDictionary()
            {
                this.alignment_dictionary = new Dictionary<string, int>();
                this.alignment_dictionary.Add("Lawful Good", 1);
                this.alignment_dictionary.Add("Neutral Good", 2);
                this.alignment_dictionary.Add("Chaotic Good", 3);
                this.alignment_dictionary.Add("Lawful Neutral", 4);
                this.alignment_dictionary.Add("True Neutral", 5);
                this.alignment_dictionary.Add("Chaotic Neutral", 6);
                this.alignment_dictionary.Add("Lawful Evil", 7);
                this.alignment_dictionary.Add("Neutral Evil", 8);
                this.alignment_dictionary.Add("Chaotic Evil", 9);
                this.alignment_dictionary_reverse = new Dictionary<int, string>();
                this.alignment_dictionary_reverse.Add(1, "Lawful Good");
                this.alignment_dictionary_reverse.Add(2, "Neutral Good");
                this.alignment_dictionary_reverse.Add(3, "Chaotic Good");
                this.alignment_dictionary_reverse.Add(4, "Lawful Neutral");
                this.alignment_dictionary_reverse.Add(5, "True Neutral");
                this.alignment_dictionary_reverse.Add(6, "Chaotic Neutral");
                this.alignment_dictionary_reverse.Add(7, "Lawful Evil");
                this.alignment_dictionary_reverse.Add(8, "Neutral Evil");
                this.alignment_dictionary_reverse.Add(9, "Chaotic Evil");
            }

            public bool save()
            {
                return save(this.name);
            }

		    public bool save(string newname){
				// Things like languages, feats etc. will be removes from the DB and repopulated
				
				repopulate();
                List<Skill> skill_list = new List<Skill>();
			    DataTable da = this.get.GetMySQLFilledTable(
				    "SELECT * FROM `character` WHERE name='" + name + "';");
			    if (da.Rows.Count > 0){
				    // character already exists, update, not insert
                    // But first make sure that name doesn't already exist
                    string db_command = ("Select * from `character` where name='" + newname + "';");
                    da = this.get.GetMySQLFilledTable(db_command);
                    if (da.Rows.Count == 0 || newname == name)
                    {
                        if (newname != name)
                        {
                            skill_list = transferSkills(newname);
                        }
                        db_command = ("UPDATE `Character` SET" +
                            " name = '" + newname + "'," +
                            " copper = '" + copper + "'," +
                            " max_hp = '" + max_hp + "'," +
                            " cur_hp = '" + cur_hp + "'," +
                            " str = '" + str + "'," +
                            " dex = '" + dex + "'," +
                            " con = '" + con + "'," +
                            " `int` = '" + intelligence + "'," +
                            " wis = '" + wis + "'," +
                            " cha = '" + cha + "'," +
                            " Race_name = '" + race + "'," +
                            " Alignment_id = '" + alignment_dictionary[this.alignment] + "' " +
                            "WHERE name = '" + name + "';");
                        this.get.GetMySQLExecuteNonQuery(db_command);
                        if (newname != name)
                        {
                            saveSkills(skill_list);
                        }
                    }
                    else
                    {
                        MessageBox.Show("You can't change a character name to an existing character name!");
                        return false;
                    }
                    return true;
			    }

			    // create character
			    string dbCommand = "INSERT INTO `Character` (name, copper, max_hp, cur_hp, str, dex, con, `int`, wis, cha, Race_name, alignment_id) values ('"
		                   + name + "', '"
                           + copper + "', '"
                           + max_hp + "', '"
                           + max_hp + "', '"
                           + str + "', '"
                           + dex + "', '"
                           + con + "', '"
                           + intelligence + "', '"
                           + wis + "', '"
                           + cha + "', '"
                           + race + "', '"
		                   + alignment_dictionary[alignment] + "');";
			    try {
				    get.GetMySQLExecuteNonQuery(dbCommand);
			    } catch (Exception e) {
				    Console.WriteLine(e);
                    MessageBox.Show("Character creation failed.");
                    return false;
			    }

                return true;
		    }
			
			private bool repopulate(){
				// remove and readd all the feats, languages, etc. a character has to the database
				// Just in case they lost some or gained some, instead of testing whether everything is in
				// the database already, and removing the ones that shouldn't be and adding the ones that should be
				
				// save feats
				// ugh, so much repeated code!  I wish I had Lispish macros to get rid of it all
				string dbcommand = "DELETE FROM `Character_has_Feat` WHERE Character_name='" + this.name + "';";
				try {
				    get.GetMySQLExecuteNonQuery(dbcommand);
			    } catch (Exception e) {
				    Console.WriteLine(e);
                    MessageBox.Show("Character saving failed, trying again");
                    return false;
			    }
				foreach(string feat in feats){
					 try {
                         get.GetMySQLExecuteNonQuery("INSERT INTO `Character_has_Feat` (Character_name, Feat_name) values ('"
				                   + name + "', '"
		                           + feat + "');");
				    } catch (Exception e) {
					    Console.WriteLine(e);
	                    MessageBox.Show("Character saving failed, trying again");
	                    return false;
				    }
				}
				
				// save languages
				dbcommand = "DELETE FROM `Character_knows_Language` WHERE Character_name='" + this.name + "';";
				try {
                    get.GetMySQLExecuteNonQuery(dbcommand);
			    } catch (Exception e) {
				    Console.WriteLine(e);
                    MessageBox.Show("Character saving failed, trying again");
                    return false;
			    }
				int langid;
				foreach(string lang in languages){
					 try {
						DataRow da = this.get.GetMySQLFilledRow(
						    "SELECT id FROM `language` WHERE name='" + lang + "';");
						langid = Convert.ToInt32(da["id"]);
					    get.GetMySQLExecuteNonQuery("INSERT INTO `Character_knows_Language` (Character_name, Language_id) values ('"
				                   + name + "', '"
		                           + langid + "');");
				    } catch (Exception e) {
					    Console.WriteLine(e);
	                    MessageBox.Show("Character saving failed.");
	                    return false;
				    }
				}
				
				// save spells
				dbcommand = "DELETE FROM `Character_has_Spell` WHERE Character_name='" + this.name + "';";
				try {
                    get.GetMySQLExecuteNonQuery(dbcommand);
			    } catch (Exception e) {
				    Console.WriteLine(e);
                    MessageBox.Show("Character saving failed, trying again");
                    return false;
			    }
				int spellid;
				foreach(Spell spell in known_spells){
					 try {
						DataRow da = this.get.GetMySQLFilledRow(
						    "SELECT id FROM `Spell` WHERE name='" + spell.getName() + "';");
						spellid = Convert.ToInt32(da["id"]);
                        get.GetMySQLExecuteNonQuery("INSERT INTO `Character_has_Spell` (Character_name, Spell_id) values ('"
				                   + name + "', '"
		                           + spellid + "');");
				    } catch (Exception e) {
					    Console.WriteLine(e);
	                    MessageBox.Show("Character saving failed.");
	                    return false;
				    }
				}
				
				// save class
				dbcommand = "DELETE FROM `Character_has_Class` WHERE Character_name='" + this.name + "';";
				try {
                    get.GetMySQLExecuteNonQuery(dbcommand);
			    } catch (Exception e) {
				    Console.WriteLine(e);
                    MessageBox.Show("Character saving failed, trying again");
                    return false;
			    }
				foreach(KeyValuePair<string, int> kvp in class_levels){
					 try {
                         get.GetMySQLExecuteNonQuery("INSERT INTO `Character_has_Class` (Character_name, Class_name, `level`) values ('"
				                   + name + "', '"
								   + kvp.Key + "', '"
		                           + kvp.Value + "');");
				    } catch (Exception e) {
					    Console.WriteLine(e);
	                    MessageBox.Show("Character saving failed.");
	                    return false;
				    }
				}
				
				// save equipment
				dbcommand = "DELETE FROM `Character_has_Equipment` WHERE Character_name='" + this.name + "';";
				try {
                    get.GetMySQLExecuteNonQuery(dbcommand);
			    } catch (Exception e) {
				    Console.WriteLine(e);
                    MessageBox.Show("Character saving failed, trying again");
                    return false;
			    }
				int eqpmntid;
				foreach(Equipment eqpmnt in equipment){
					 try {
						DataRow da = this.get.GetMySQLFilledRow(
						    "SELECT id FROM `equipment` WHERE name='" + eqpmnt.getEquipmentName() + "';");
						eqpmntid = Convert.ToInt32(da["id"]);
						
						
					    get.GetMySQLExecuteNonQuery("INSERT INTO `Character_has_Equipment` (Character_name, Equipment_id, quantity, equipped) values ('"
				                   + name + "', '"
								   + eqpmntid + "', '"
								   + eqpmnt.getQuantity() + "', "
		                           + eqpmnt.getEquipped().ToString() + ");");
				    } catch (Exception e) {
					    
				    }
				}
                get.Connection.Close();
                return true;
			}

            private List<Skill> transferSkills(string newname)
            {
                List<Skill> skill_list = new List<Skill>();
                string dbcommand = "Select * from character_knows_skill where Character_name='" + this.name + "';";
                DataTable dt = get.GetMySQLFilledTable(dbcommand);
                foreach (DataRow row in dt.Rows)
                {
                    Skill skill = new Skill(Convert.ToInt32(row["Skill_id"]), Convert.ToInt32(row["ranks"]));
                    skill.setCharacterName(newname);
                    skill_list.Add(skill);
                }
                dbcommand = "DELETE from character_knows_skill where character_name='" + this.name + "';";
                get.GetMySQLExecuteNonQuery(dbcommand);
                return skill_list;
            }

            private void saveSkills(List<Skill> skill_list)
            {
                foreach (Skill skill in skill_list)
                {
                    skill.save();
                }
            }

            public void load()
            {
                string dbCommand = "SELECT * FROM `character` WHERE name='" + this.name + "';";
                DataRow dt = get.GetMySQLFilledRow(dbCommand);
                this.copper = Convert.ToInt32(dt["copper"]);
                this.max_hp = Convert.ToInt32(dt["max_hp"]);
                this.cur_hp = Convert.ToInt32(dt["cur_hp"]);
                this.str = Convert.ToInt32(dt["str"]);
                this.dex = Convert.ToInt32(dt["dex"]);
                this.con = Convert.ToInt32(dt["con"]);
                this.intelligence = Convert.ToInt32(dt["int"]);
                this.wis = Convert.ToInt32(dt["wis"]);
                this.cha = Convert.ToInt32(dt["cha"]);
                this.race = dt["Race_name"].ToString();
                this.alignment = this.alignment_dictionary_reverse[Convert.ToInt32(dt["alignment_id"])];
				
				DataTable tb;
				// get languages
				languages = new List<string>();
				dbCommand = "SELECT lang.name FROM `language` lang, Character_knows_Language ckl"+
				" WHERE ckl.Character_name='" + this.name + "' AND ckl.Language_id = lang.id;";
				tb = get.GetMySQLFilledTable(dbCommand);
				foreach (DataRow row in tb.Rows) {
					languages.Add(row["name"].ToString());
				}
				
				// get feats
				feats = new List<string>();
				dbCommand = "SELECT ckf.Feat_name FROM `Character_has_Feat` ckf"+
					" WHERE ckf.Character_name='" + this.name + "';";
				tb = get.GetMySQLFilledTable(dbCommand);
				foreach (DataRow row in tb.Rows) {
					feats.Add(row["Feat_name"].ToString());
				}
				// get spells
				known_spells = new List<Spell>();
				dbCommand = "SELECT spell.name FROM `Character_has_Spell` chs, `Spell` spell"+
					" WHERE chs.Character_name='" + this.name + "' AND chs.Spell_id  = spell.id;";
				tb = get.GetMySQLFilledTable(dbCommand);
				foreach (DataRow row in tb.Rows) {
					known_spells.Add(new Spell( row["name"].ToString() ));
				}
				
				//get equipment
				this.equipment = new List<Equipment>();
				dbCommand = "SELECT equip.name, che.quantity, che.equipped FROM `Character_has_Equipment` che, `Equipment` equip"+
					" WHERE che.Character_name='" + this.name + "' AND che.Equipment_id  = equip.id;";
				tb = get.GetMySQLFilledTable(dbCommand);
				Equipment toAdd;
				foreach (DataRow row in tb.Rows) {
					toAdd = new Equipment( row["name"].ToString(), Convert.ToBoolean(row["equipped"]));
					toAdd.setEquipped(Convert.ToBoolean(row["equipped"]));
					toAdd.setQuantity(Convert.ToInt32(row["quantity"]));
					equipment.Add(toAdd);
				}
				
				//get armor
				this.armor = new List<Armor>();
				dbCommand = "SELECT equip.name, che.quantity, che.equipped FROM `Character_has_Equipment` che, `Equipment` equip, "+
					"`Equipment_is_Armor` eia WHERE che.Character_name='" + this.name +
					"' AND che.Equipment_id  = equip.id AND eia.Equipment_id = equip.id;";
				tb = get.GetMySQLFilledTable(dbCommand);
				Armor armortoAdd;
				foreach (DataRow row in tb.Rows) {
					armortoAdd = new Armor(row["name"].ToString());
					armortoAdd.setEquipped(Convert.ToBoolean(row["equipped"]));
					armortoAdd.setQuantity(Convert.ToInt32(row["quantity"]));
					armor.Add(armortoAdd);
				}
				
				//get weapons
				this.weapons = new List<Weapon>();
				dbCommand = "SELECT equip.name, che.quantity, che.equipped FROM `Character_has_Equipment` che, `Equipment` equip, "+
					"`Equipment_is_Weapon` eiw WHERE che.Character_name='" + this.name +
					"' AND che.Equipment_id  = equip.id AND eiw.Equipment_id = equip.id;";
				tb = get.GetMySQLFilledTable(dbCommand);
				Weapon weapontoAdd;
				foreach (DataRow row in tb.Rows) {
					weapontoAdd = new Weapon( row["name"].ToString());
                    weapontoAdd.setEquipped(Convert.ToBoolean(row["equipped"]));
                    weapontoAdd.setQuantity(Convert.ToInt32(row["quantity"]));
                    weapons.Add(weapontoAdd);
				}
				
				
				
				// get levels
				this.class_levels = new Dictionary<string, int>();
				dbCommand = "SELECT * FROM `Character_has_Class` chc "+
					" WHERE chc.Character_name='" + this.name + "';";
				tb = get.GetMySQLFilledTable(dbCommand);
				foreach (DataRow row in tb.Rows) {
					class_levels.Add(row["Class_name"].ToString(), 
									 Convert.ToInt32(row["Class_Level"]));
				}
				
            }
    		
		    // derive the derived attributes
            public void deriveAttributes()
            {
                // first, get the characters AC
                ac = 10;
                ac += (dex - 10) / 2;
                //check c# foreach syntax
                foreach (Armor armor in this.armor)
                {
                    ac += armor.getACbonus();
                }

                bab = 0; //Base Attack Bonus
                fort = (con - 10) / 2;
                will = (wis - 10) / 2;
                reflex = (dex - 10) / 2;
                DataTable tb;

                foreach (KeyValuePair<string, int> kvp in class_levels)
                {
                    string query = "SELECT * FROM 'Class' c WHERE c.name" + kvp.Key +
                        " c.level = " + kvp.Value;
                    tb = get.GetMySQLFilledTable(query);
                    foreach (DataRow row in tb.Rows)
                    {
                        // should only run once, since there query is
                        // on the primary key...
                        bab += Convert.ToInt32(row["bab"]);
                        fort += Convert.ToInt32(row["fortitude"]);
                        will += Convert.ToInt32(row["will"]);
                        reflex += Convert.ToInt32(row["reflex"]);
                    }
                }
            }
    		
			public static List<string> getPossiblePartyMembers(string name){
				getMySQL get = new getMySQL();
                List<string> toReturn = new List<string>();
				string dbcommand = "SELECT name FROM `Character` where name <> '" +name+"';";
				DataTable dt = get.GetMySQLFilledTable(dbcommand);
				foreach (DataRow row in dt.Rows){
					toReturn.Add(row["name"].ToString());
				}
                return toReturn;
			}
				
		    public List<string> getLanguages(){
			    return languages;
		    }
		    public void addLanguage(string lang){
				if(!languages.Contains(lang)){
					languages.Add(lang);
				}
		    }
    		
		    public void setLanguage( List<string> languages ){
			    this.languages = languages;
		    }
    		
		    public List<string> getFeats(){
			    return feats;
		    }
		    public void addFeat(string feat){
			    if(!feats.Contains(feat)){feats.Add(feat);}
		    }
    		
		    public void setFeats( List<string> feats ){
			    this.feats = feats;
		    }
    		
		    public List<Spell> getKnownSpells(){
			    return known_spells;
		    }
		    public void addKnownSpell(Spell spell){
			    if(!known_spells.Contains(spell)){known_spells.Add(spell);}
		    }
    		
		    public void setSpells( List<Spell> known_spells ){
			    this.known_spells = known_spells;
		    }
    		
		    public List<Equipment> getEquipment(){
			    return equipment;
		    }
		    public void addEquipment(Equipment equip){
			    if(!equipment.Contains(equip)){equipment.Add(equip);}
		    }
    		
		    public void setEquipment( List<Equipment> equipment ){
			    this.equipment = equipment;
		    }
    		
		    public List<Armor> getArmor(){
			    return armor;
		    }
		    public void addArmor(Armor armor){
				if(!this.armor.Contains(armor)){
			    	this.armor.Add(armor);
                	this.addEquipment(armor);
			    	deriveAttributes();
				}
		    }
    		
		    public void setArmor( List<Armor> armor ){
			    this.armor = armor;
			    deriveAttributes();
		    }
    		
		    public List<Weapon> getWeapons(){
			    return weapons;
		    }
		    public void addWeapon(Weapon weapon){
				if(!this.weapons.Contains(weapon)){
			    	weapons.Add(weapon);
                	this.addEquipment(weapon);
				}
		    }
    		
		    public void setWeapons( List<Weapon> weapons){
			    this.weapons = weapons;
		    }
		    public Dictionary<String, int> getLevels(){
			    return class_levels;
		    }
		    public void addLevel(string charClass, int level){
			    if(class_levels.ContainsKey(charClass)){
				    class_levels.Remove(charClass);
			    }
			    class_levels.Add(charClass, level);
			    deriveAttributes();
		    }
    		
		    public void setLevels ( Dictionary<String, int> levels ){
			    class_levels = levels;
			    deriveAttributes();
		    }
    		
		    public String getName(){
			    return name;
		    }
    		
		    public void setName( String name ){
			    this.name = name;
		    }
    		
		    public String getRace(){
			    return race;
		    }

            public string getAlignment()
            {
                return this.alignment;
            }
            public void setAlignment(string alignment)
            {
                this.alignment = alignment;
            }
		    public void setRace( String race ){
			    this.race = race;
		    }
    		
		    public int getCopper(){
			    return copper;
		    }
    		
		    public void setCopper( int copper ){
			    this.copper = copper;
		    }
    		
		    public int getCur_hp(){
			    return cur_hp;
		    }
    		
		    public void setCur_hp( int cur_hp ){
			    this.cur_hp = cur_hp;
		    }
    		
		    public int getMax_hp(){
			    return max_hp;
		    }
    		
		    public void setMax_hp( int max_hp ){
			    this.max_hp = max_hp;
		    }
    		
		    public int getStr(){
			    return str;
		    }
    		
		    public void setStr( int str ){
			    this.str = str;
			    deriveAttributes();
		    }
    		
		    public int getDex(){
			    return dex;
		    }
    		
		    public void setDex( int dex ){
			    this.dex = dex;
			    deriveAttributes();
		    }
    		
		    public int getCon(){
			    return con;
		    }
    		
		    public void setCon( int con ){
			    this.con = con;
			    deriveAttributes();
		    }
    		
		    public int getWis(){
			    return wis;
		    }
    		
		    public void setWis( int wis ){
			    this.wis = wis;
			    deriveAttributes();
		    }
    		
		    public int getIntelligence(){
			    return intelligence;
		    }
    		
		    public void setIntelligence( int intelligence ){
			    this.intelligence = intelligence;
		    }
    		
		    public int getCha(){
			    return cha;
		    }
    		
		    public void setCha( int cha ){
			    this.cha = cha;
		    }
    		
		    public int getBab(){
			    return bab;
		    }
    		
		    public int getFort(){
			    return fort;
		    }
    		
		    public int getReflex(){
			    return reflex;
		    }
    		
		    public int getWill(){
			    return will;
		    }
    		
		    public int getAc(){
			    return ac;
		    }
    }
}
